使用声明

  当某个名字在它自己的名字空间之外频繁使用时,在反复写它的时候都要加上有关名字空间作为限定词,这样做也会变成很令人厌烦的事情。考虑

    double Parser::prim(bool get)        // 处理初等项
    {
        if(get) Lexer::get_token();

        switch(Lexer::curr_tok) {
        case Lexer::NUMBER:              // 浮点常量
             Lexer::get_token();
             return Lexer::number_value;
        case Lexer::NAME:
        {
            double& v = table[Lexer::string_value];
            if(Lexer::get_token() == Lexer::ASSIGN) v = expr(true);
            return v;
        }
        case Lexer::MINUS:               // 一元减
             return -prim(true);
        case Lexer::LP:
        {
            double e = expr(true);
            if(Lexer::curr_tok != Lexer::RP) return Error::error(") expected");
            Lexer::get_token();          // 吃掉 ')'
            return e;
        }
        case Lexer::END:
             return 1;
        default:
             return Error::error("primary expected");
        }
    }

反复写限定词Lexer很讨厌,也会分散人的注意力。这种多余的东西可以通过一个使用声明而清除掉,只需要在某个地方说明,在这个作用域里所用的get_token就是Lexer的get_token。例如,

    double Parser::prim(bool get)        // 处理初等项
    {
        using Lexer::get_token;       // 使用Lexer的get_token
        using Lexer::curr_tok;        // 使用Lexer的curr_tok
        using Error::error;           // 使用Error的error

        if(get) get_token();

        switch(curr_tok) {
        case Lexer::NUMBER:              // 浮点常量
             get_token();
             return Lexer::number_value;
        case Lexer::NAME:
        {
            double& v = table[Lexer::string_value];
            if(get_token() == Lexer::ASSIGN) v = expr(true);
            return v;
        }
        case Lexer::MINUS:               // 一元减
             return -prim(true);
        case Lexer::LP:
        {
            double e = expr(true);
            if(curr_tok != Lexer::RP) return error(") expected");
            get_token();          // 吃掉 ')'
            return e;
        }
        case Lexer::END:
             return 1;
        default:
             return error("primary expected");
        }
    }

使用声明将引进局部的同义词。

  将同义词尽可能地保持在局部中以避免混乱,这是一种很好的想法。由于所有分析函数都使用差不多同样的一集来自其他模块的名字,我们也可以把有关的使用声明放在Parser名字空间的定义里:

    namespace Parser {
        double prim(bool);
        double term(bool);
        double expr(bool);

        using Lexer::get_token;        // 使用Lexer的get_token
        using Lexer::curr_tok;         // 使用Lexer的curr_tok
        using Error::error;            // 使用Error的error
    }

这就使我们能将Parser函数简化到几乎等同于原来的版本了:

    double Parser::term(bool get)        // 乘和除
    {
        double left = prim(get);

        for(;;)
            switch(curr_tok) {
            case Lexer::MUL:
                left *= prim(true);
                break;
            case Lexer::DIV:
                if(double d = prim(true)) {
                    left /= d;
                    break;
                }
                return error("divide by 0");
            default:
                return left;
            }
    }

也可以将各个单词的名字引进Parser名字空间。然而我还是采用带显式限定词的形式把它们放在那里,以帮助我们记住Parser依赖于Lexer。

🔚